1 /** 2 * 测试用例库文件,提供如event mock、iframe封装等各种常用功能 部分方法来源于YUI测试框架 3 */ 4 UserAction = { 5 beforedispatch:null, 6 // flag : true, 7 isf /* is function ? */:function ( value ) { 8 return value && (typeof value == 'function'); 9 }, 10 isb /* is boolean? */:function ( value ) { 11 return value && (typeof value == 'boolean'); 12 }, 13 iso /* is object? */:function ( value ) { 14 return value && (typeof value == 'object'); 15 }, 16 iss /* is string? */:function ( value ) { 17 return value && (typeof value == 'string'); 18 }, 19 isn /* is number? */:function ( value ) { 20 return value && (typeof value == 'number'); 21 }, 22 // -------------------------------------------------------------------------- 23 // Generic event methods 24 // -------------------------------------------------------------------------- 25 26 /** 27 * Simulates a key event using the given event information to populate the 28 * generated event object. This method does browser-equalizing calculations 29 * to account for differences in the DOM and IE event models as well as 30 * different browser quirks. Note: keydown causes Safari 2.x to crash. 31 * 32 * @method simulateKeyEvent 33 * @private 34 * @static 35 * @param {HTMLElement} 36 * target The target of the given event. 37 * @param {String} 38 * type The type of event to fire. This can be any one of the 39 * following: keyup, keydown, and keypress. 40 * @param {Boolean} 41 * bubbles (Optional) Indicates if the event can be bubbled up. 42 * DOM Level 3 specifies that all key events bubble by default. 43 * The default is true. 44 * @param {Boolean} 45 * cancelable (Optional) Indicates if the event can be canceled 46 * using preventDefault(). DOM Level 3 specifies that all key 47 * events can be cancelled. The default is true. 48 * @param {Window} 49 * view (Optional) The view containing the target. This is 50 * typically the window object. The default is window. 51 * @param {Boolean} 52 * ctrlKey (Optional) Indicates if one of the CTRL keys is 53 * pressed while the event is firing. The default is false. 54 * @param {Boolean} 55 * altKey (Optional) Indicates if one of the ALT keys is pressed 56 * while the event is firing. The default is false. 57 * @param {Boolean} 58 * shiftKey (Optional) Indicates if one of the SHIFT keys is 59 * pressed while the event is firing. The default is false. 60 * @param {Boolean} 61 * metaKey (Optional) Indicates if one of the META keys is 62 * pressed while the event is firing. The default is false. 63 * @param {int} 64 * keyCode (Optional) The code for the key that is in use. The 65 * default is 0. 66 * @param {int} 67 * charCode (Optional) The Unicode code for the character 68 * associated with the key being used. The default is 0. 69 */ 70 simulateKeyEvent:function ( target /* :HTMLElement */, type /* :String */, bubbles /* :Boolean */, cancelable /* :Boolean */, view /* :Window */, ctrlKey /* :Boolean */, altKey /* :Boolean */, shiftKey /* :Boolean */, metaKey /* :Boolean */, keyCode /* :int */, charCode /* :int */ ) /* :Void */ { 71 // check target 72 target = typeof target == 'string' ? document.getElementById( target ) 73 : target; 74 if ( !target ) { 75 throw new Error( "simulateKeyEvent(): Invalid target." ); 76 } 77 78 // check event type 79 if ( typeof type == 'string' ) { 80 type = type.toLowerCase(); 81 switch ( type ) { 82 case "keyup": 83 case "keydown": 84 case "keypress": 85 break; 86 case "textevent": // DOM Level 3 87 type = "keypress"; 88 break; 89 // @TODO was the fallthrough intentional, if so throw error 90 default: 91 throw new Error( "simulateKeyEvent(): Event type '" + type 92 + "' not supported." ); 93 } 94 } else { 95 throw new Error( "simulateKeyEvent(): Event type must be a string." ); 96 } 97 98 // setup default values 99 if ( !this.isb( bubbles ) ) { 100 bubbles = true; // all key events bubble 101 } 102 if ( !this.isb( cancelable ) ) { 103 cancelable = true; // all key events can be cancelled 104 } 105 if ( !this.iso( view ) ) { 106 view = window; // view is typically window 107 } 108 if ( !this.isb( ctrlKey ) ) { 109 ctrlKey = false; 110 } 111 if ( !this.isb( typeof altKey == 'boolean' ) ) { 112 altKey = false; 113 } 114 if ( !this.isb( shiftKey ) ) { 115 shiftKey = false; 116 } 117 if ( !this.isb( metaKey ) ) { 118 metaKey = false; 119 } 120 if ( !(typeof keyCode == 'number') ) { 121 keyCode = 0; 122 } 123 if ( !(typeof charCode == 'number') ) { 124 charCode = 0; 125 } 126 127 // try to create a mouse event 128 var customEvent /* :MouseEvent */ = null; 129 130 // check for DOM-compliant browsers first 131 if ( this.isf( document.createEvent ) ) { 132 133 try { 134 135 // try to create key event 136 customEvent = document.createEvent( "KeyEvents" ); 137 138 /* 139 * Interesting problem: Firefox implemented a non-standard 140 * version of initKeyEvent() based on DOM Level 2 specs. Key 141 * event was removed from DOM Level 2 and re-introduced in DOM 142 * Level 3 with a different interface. Firefox is the only 143 * browser with any implementation of Key Events, so for now, 144 * assume it's Firefox if the above line doesn't error. 145 */ 146 // TODO: Decipher between Firefox's implementation and a correct 147 // one. 148 customEvent.initKeyEvent( type, bubbles, cancelable, view, 149 ctrlKey, altKey, shiftKey, metaKey, keyCode, charCode ); 150 151 } catch ( ex /* :Error */ ) { 152 153 /* 154 * If it got here, that means key events aren't officially 155 * supported. Safari/WebKit is a real problem now. WebKit 522 156 * won't let you set keyCode, charCode, or other properties if 157 * you use a UIEvent, so we first must try to create a generic 158 * event. The fun part is that this will throw an error on 159 * Safari 2.x. The end result is that we need another 160 * try...catch statement just to deal with this mess. 161 */ 162 try { 163 164 // try to create generic event - will fail in Safari 2.x 165 customEvent = document.createEvent( "Events" ); 166 167 } catch ( uierror /* :Error */ ) { 168 169 // the above failed, so create a UIEvent for Safari 2.x 170 customEvent = document.createEvent( "UIEvents" ); 171 172 } finally { 173 174 customEvent.initEvent( type, bubbles, cancelable ); 175 176 // initialize 177 customEvent.view = view; 178 customEvent.altKey = altKey; 179 customEvent.ctrlKey = ctrlKey; 180 customEvent.shiftKey = shiftKey; 181 customEvent.metaKey = metaKey; 182 customEvent.keyCode = keyCode; 183 customEvent.charCode = charCode; 184 185 } 186 187 } 188 189 // before dispatch 190 if ( this.beforedispatch && typeof this.beforedispatch == 'function' ) 191 this.beforedispatch( customEvent ); 192 this.beforedispatch = null; 193 194 // fire the event 195 target.dispatchEvent( customEvent ); 196 197 } else if ( this.iso( document.createEventObject ) ) { // IE 198 199 // create an IE event object 200 customEvent = document.createEventObject(); 201 202 // assign available properties 203 customEvent.bubbles = bubbles; 204 customEvent.cancelable = cancelable; 205 customEvent.view = view; 206 customEvent.ctrlKey = ctrlKey; 207 customEvent.altKey = altKey; 208 customEvent.shiftKey = shiftKey; 209 customEvent.metaKey = metaKey; 210 211 /* 212 * IE doesn't support charCode explicitly. CharCode should take 213 * precedence over any keyCode value for accurate representation. 214 */ 215 customEvent.keyCode = (charCode > 0) ? charCode : keyCode; 216 217 // before dispatch 218 if ( this.beforedispatch && typeof this.beforedispatch == 'function' ) 219 this.beforedispatch( customEvent ); 220 this.beforedispatch = null; 221 222 // fire the event 223 target.fireEvent( "on" + type, customEvent ); 224 225 } else { 226 throw new Error( 227 "simulateKeyEvent(): No event simulation framework present." ); 228 } 229 230 this.beforedispatch = null; 231 }, 232 233 /** 234 * Simulates a mouse event using the given event information to populate the 235 * generated event object. This method does browser-equalizing calculations 236 * to account for differences in the DOM and IE event models as well as 237 * different browser quirks. 238 * 239 * @method simulateMouseEvent 240 * @private 241 * @static 242 * @param {HTMLElement} 243 * target The target of the given event. 244 * @param {String} 245 * type The type of event to fire. This can be any one of the 246 * following: click, dblclick, mousedown, mouseup, mouseout, 247 * mouseover, and mousemove. 248 * @param {Boolean} 249 * bubbles (Optional) Indicates if the event can be bubbled up. 250 * DOM Level 2 specifies that all mouse events bubble by default. 251 * The default is true. 252 * @param {Boolean} 253 * cancelable (Optional) Indicates if the event can be canceled 254 * using preventDefault(). DOM Level 2 specifies that all mouse 255 * events except mousemove can be cancelled. The default is true 256 * for all events except mousemove, for which the default is 257 * false. 258 * @param {Window} 259 * view (Optional) The view containing the target. This is 260 * typically the window object. The default is window. 261 * @param {int} 262 * detail (Optional) The number of times the mouse button has 263 * been used. The default value is 1. 264 * @param {int} 265 * screenX (Optional) The x-coordinate on the screen at which 266 * point the event occured. The default is 0. 267 * @param {int} 268 * screenY (Optional) The y-coordinate on the screen at which 269 * point the event occured. The default is 0. 270 * @param {int} 271 * clientX (Optional) The x-coordinate on the client at which 272 * point the event occured. The default is 0. 273 * @param {int} 274 * clientY (Optional) The y-coordinate on the client at which 275 * point the event occured. The default is 0. 276 * @param {Boolean} 277 * ctrlKey (Optional) Indicates if one of the CTRL keys is 278 * pressed while the event is firing. The default is false. 279 * @param {Boolean} 280 * altKey (Optional) Indicates if one of the ALT keys is pressed 281 * while the event is firing. The default is false. 282 * @param {Boolean} 283 * shiftKey (Optional) Indicates if one of the SHIFT keys is 284 * pressed while the event is firing. The default is false. 285 * @param {Boolean} 286 * metaKey (Optional) Indicates if one of the META keys is 287 * pressed while the event is firing. The default is false. 288 * @param {int} 289 * button (Optional) The button being pressed while the event is 290 * executing. The value should be 0 for the primary mouse button 291 * (typically the left button), 1 for the terciary mouse button 292 * (typically the middle button), and 2 for the secondary mouse 293 * button (typically the right button). The default is 0. 294 * @param {HTMLElement} 295 * relatedTarget (Optional) For mouseout events, this is the 296 * element that the mouse has moved to. For mouseover events, 297 * this is the element that the mouse has moved from. This 298 * argument is ignored for all other events. The default is null. 299 */ 300 simulateMouseEvent:function ( target /* :HTMLElement */, type /* :String */, bubbles /* :Boolean */, cancelable /* :Boolean */, view /* :Window */, detail /* :int */, screenX /* :int */, screenY /* :int */, clientX /* :int */, clientY /* :int */, ctrlKey /* :Boolean */, altKey /* :Boolean */, shiftKey /* :Boolean */, metaKey /* :Boolean */, button /* :int */, relatedTarget /* :HTMLElement */ ) /* :Void */ { 301 302 // check target 303 target = typeof target == 'string' ? document.getElementById( target ) 304 : target; 305 if ( !target ) { 306 throw new Error( "simulateMouseEvent(): Invalid target." ); 307 } 308 309 // check event type 310 if ( this.iss( type ) ) { 311 type = type.toLowerCase(); 312 switch ( type ) { 313 case "mouseover": 314 case "mouseout": 315 case "mousedown": 316 case "mouseup": 317 case "click": 318 case "dblclick": 319 case "mousemove": 320 case "mouseenter":// 非标准支持,仅为测试提供,该项仅IE下work 321 case "mouseleave": 322 break; 323 default: 324 throw new Error( "simulateMouseEvent(): Event type '" + type 325 + "' not supported." ); 326 } 327 } else { 328 throw new Error( 329 "simulateMouseEvent(): Event type must be a string." ); 330 } 331 332 // setup default values 333 if ( !this.isb( bubbles ) ) { 334 bubbles = true; // all mouse events bubble 335 } 336 if ( !this.isb( cancelable ) ) { 337 cancelable = (type != "mousemove"); // mousemove is the only one 338 // that can't be cancelled 339 } 340 if ( !this.iso( view ) ) { 341 view = window; // view is typically window 342 } 343 if ( !this.isn( detail ) ) { 344 detail = 1; // number of mouse clicks must be at least one 345 } 346 if ( !this.isn( screenX ) ) { 347 screenX = 0; 348 } 349 if ( !this.isn( screenY ) ) { 350 screenY = 0; 351 } 352 if ( !this.isn( clientX ) ) { 353 clientX = 0; 354 } 355 if ( !this.isn( clientY ) ) { 356 clientY = 0; 357 } 358 if ( !this.isb( ctrlKey ) ) { 359 ctrlKey = false; 360 } 361 if ( !this.isb( altKey ) ) { 362 altKey = false; 363 } 364 if ( !this.isb( shiftKey ) ) { 365 shiftKey = false; 366 } 367 if ( !this.isb( metaKey ) ) { 368 metaKey = false; 369 } 370 if ( !this.isn( button ) ) { 371 button = 0; 372 } 373 374 // try to create a mouse event 375 var customEvent /* :MouseEvent */ = null; 376 377 // check for DOM-compliant browsers first 378 if ( this.isf( document.createEvent ) ) { 379 380 customEvent = document.createEvent( "MouseEvents" ); 381 382 // Safari 2.x (WebKit 418) still doesn't implement initMouseEvent() 383 if ( this.browser.ie !== 9 && customEvent.initMouseEvent ) { 384 customEvent.initMouseEvent( type, bubbles, cancelable, view, 385 detail, screenX, screenY, clientX, clientY, ctrlKey, 386 altKey, shiftKey, metaKey, button, relatedTarget ); 387 } else { // Safari 388 389 // the closest thing available in Safari 2.x is UIEvents 390 customEvent = document.createEvent( "UIEvents" ); 391 customEvent.initEvent( type, bubbles, cancelable ); 392 customEvent.view = view; 393 customEvent.detail = detail; 394 customEvent.screenX = screenX; 395 customEvent.screenY = screenY; 396 customEvent.clientX = clientX; 397 customEvent.clientY = clientY; 398 customEvent.ctrlKey = ctrlKey; 399 customEvent.altKey = altKey; 400 customEvent.metaKey = metaKey; 401 customEvent.shiftKey = shiftKey; 402 customEvent.button = button; 403 customEvent.relatedTarget = relatedTarget; 404 } 405 406 /* 407 * Check to see if relatedTarget has been assigned. Firefox versions 408 * less than 2.0 don't allow it to be assigned via initMouseEvent() 409 * and the property is readonly after event creation, so in order to 410 * keep YAHOO.util.getRelatedTarget() working, assign to the IE 411 * proprietary toElement property for mouseout event and fromElement 412 * property for mouseover event. 413 */ 414 if ( relatedTarget && !customEvent.relatedTarget ) { 415 if ( type == "mouseout" ) { 416 customEvent.toElement = relatedTarget; 417 } else if ( type == "mouseover" ) { 418 customEvent.fromElement = relatedTarget; 419 } 420 } 421 422 // before dispatch 423 if ( this.beforedispatch && typeof this.beforedispatch == 'function' ) 424 this.beforedispatch( customEvent ); 425 this.beforedispatch = null; 426 427 // fire the event 428 target.dispatchEvent( customEvent ); 429 430 } else if ( this.iso( document.createEventObject ) ) { // IE 431 432 // create an IE event object 433 customEvent = document.createEventObject(); 434 435 // assign available properties 436 customEvent.bubbles = bubbles; 437 customEvent.cancelable = cancelable; 438 customEvent.view = view; 439 customEvent.detail = detail; 440 customEvent.screenX = screenX; 441 customEvent.screenY = screenY; 442 customEvent.clientX = clientX; 443 customEvent.clientY = clientY; 444 customEvent.ctrlKey = ctrlKey; 445 customEvent.altKey = altKey; 446 customEvent.metaKey = metaKey; 447 customEvent.shiftKey = shiftKey; 448 449 // fix button property for IE's wacky implementation 450 switch ( button ) { 451 case 0: 452 customEvent.button = 1; 453 break; 454 case 1: 455 customEvent.button = 4; 456 break; 457 case 2: 458 // leave as is 459 break; 460 default: 461 customEvent.button = 0; 462 } 463 464 /* 465 * Have to use relatedTarget because IE won't allow assignment to 466 * toElement or fromElement on generic events. This keeps 467 * YAHOO.util.customEvent.getRelatedTarget() functional. 468 */ 469 customEvent.relatedTarget = relatedTarget; 470 471 // before dispatch 472 if ( this.beforedispatch && typeof this.beforedispatch == 'function' ) 473 this.beforedispatch( customEvent ); 474 this.beforedispatch = null; 475 // fire the event 476 target.fireEvent( "on" + type, customEvent ); 477 478 } else { 479 throw new Error( 480 "simulateMouseEvent(): No event simulation framework present." ); 481 } 482 }, 483 484 // -------------------------------------------------------------------------- 485 // Mouse events 486 // -------------------------------------------------------------------------- 487 488 /** 489 * Simulates a mouse event on a particular element. 490 * 491 * @param {HTMLElement} 492 * target The element to click on. 493 * @param {String} 494 * type The type of event to fire. This can be any one of the 495 * following: click, dblclick, mousedown, mouseup, mouseout, 496 * mouseover, and mousemove. 497 * @param {Object} 498 * options Additional event options (use DOM standard names). 499 * @method mouseEvent 500 * @static 501 */ 502 fireMouseEvent:function ( target /* :HTMLElement */, type /* :String */, options /* :Object */ ) /* :Void */ { 503 options = options || {}; 504 this.simulateMouseEvent( target, type, options.bubbles, 505 options.cancelable, options.view, options.detail, 506 options.screenX, options.screenY, options.clientX, 507 options.clientY, options.ctrlKey, options.altKey, 508 options.shiftKey, options.metaKey, options.button, 509 options.relatedTarget ); 510 }, 511 512 /** 513 * Simulates a click on a particular element. 514 * 515 * @param {HTMLElement} 516 * target The element to click on. 517 * @param {Object} 518 * options Additional event options (use DOM standard names). 519 * @method click 520 * @static 521 */ 522 click:function ( target /* :HTMLElement */, options /* :Object */ ) /* :Void */ { 523 this.fireMouseEvent( target, "click", options ); 524 }, 525 526 /** 527 * Simulates a double click on a particular element. 528 * 529 * @param {HTMLElement} 530 * target The element to double click on. 531 * @param {Object} 532 * options Additional event options (use DOM standard names). 533 * @method dblclick 534 * @static 535 */ 536 dblclick:function ( target /* :HTMLElement */, options /* :Object */ ) /* :Void */ { 537 this.fireMouseEvent( target, "dblclick", options ); 538 }, 539 540 /** 541 * Simulates a mousedown on a particular element. 542 * 543 * @param {HTMLElement} 544 * target The element to act on. 545 * @param {Object} 546 * options Additional event options (use DOM standard names). 547 * @method mousedown 548 * @static 549 */ 550 mousedown:function ( target /* :HTMLElement */, options /* Object */ ) /* :Void */ { 551 this.fireMouseEvent( target, "mousedown", options ); 552 }, 553 554 /** 555 * Simulates a mousemove on a particular element. 556 * 557 * @param {HTMLElement} 558 * target The element to act on. 559 * @param {Object} 560 * options Additional event options (use DOM standard names). 561 * @method mousemove 562 * @static 563 */ 564 mousemove:function ( target /* :HTMLElement */, options /* Object */ ) /* :Void */ { 565 this.fireMouseEvent( target, "mousemove", options ); 566 }, 567 568 /** 569 * Simulates a mouseout event on a particular element. Use "relatedTarget" 570 * on the options object to specify where the mouse moved to. Quirks: 571 * Firefox less than 2.0 doesn't set relatedTarget properly, so toElement is 572 * assigned in its place. IE doesn't allow toElement to be be assigned, so 573 * relatedTarget is assigned in its place. Both of these concessions allow 574 * YAHOO.util.Event.getRelatedTarget() to work correctly in both browsers. 575 * 576 * @param {HTMLElement} 577 * target The element to act on. 578 * @param {Object} 579 * options Additional event options (use DOM standard names). 580 * @method mouseout 581 * @static 582 */ 583 mouseout:function ( target /* :HTMLElement */, options /* Object */ ) /* :Void */ { 584 this.fireMouseEvent( target, "mouseout", options ); 585 }, 586 587 /** 588 * Simulates a mouseover event on a particular element. Use "relatedTarget" 589 * on the options object to specify where the mouse moved from. Quirks: 590 * Firefox less than 2.0 doesn't set relatedTarget properly, so fromElement 591 * is assigned in its place. IE doesn't allow fromElement to be be assigned, 592 * so relatedTarget is assigned in its place. Both of these concessions 593 * allow YAHOO.util.Event.getRelatedTarget() to work correctly in both 594 * browsers. 595 * 596 * @param {HTMLElement} 597 * target The element to act on. 598 * @param {Object} 599 * options Additional event options (use DOM standard names). 600 * @method mouseover 601 * @static 602 */ 603 mouseover:function ( target /* :HTMLElement */, options /* Object */ ) /* :Void */ { 604 this.fireMouseEvent( target, "mouseover", options ); 605 }, 606 607 /** 608 * Simulates a mouseup on a particular element. 609 * 610 * @param {HTMLElement} 611 * target The element to act on. 612 * @param {Object} 613 * options Additional event options (use DOM standard names). 614 * @method mouseup 615 * @static 616 */ 617 mouseup:function ( target /* :HTMLElement */, options /* Object */ ) /* :Void */ { 618 this.fireMouseEvent( target, "mouseup", options ); 619 }, 620 621 dragto:function ( target, options ) { 622 var me = this; 623 me.mousemove( target, { 624 clientX:options.startX, 625 clientY:options.startY 626 } ); 627 setTimeout( function () { 628 me.mousedown( target, { 629 clientX:options.startX, 630 clientY:options.startY 631 } ); 632 setTimeout( function () { 633 me.mousemove( target, { 634 clientX:options.endX, 635 clientY:options.endY 636 } ); 637 setTimeout( function () { 638 me.mouseup( target, { 639 clientX:options.endX, 640 clientY:options.endY 641 } ); 642 if ( options.callback ) 643 options.callback(); 644 }, options.aftermove || 20 ); 645 }, options.beforemove || 20 ); 646 }, options.beforestart || 50 ); 647 }, 648 649 // -------------------------------------------------------------------------- 650 // Key events 651 // -------------------------------------------------------------------------- 652 653 /** 654 * Fires an event that normally would be fired by the keyboard (keyup, 655 * keydown, keypress). Make sure to specify either keyCode or charCode as an 656 * option. 657 * 658 * @private 659 * @param {String} 660 * type The type of event ("keyup", "keydown" or "keypress"). 661 * @param {HTMLElement} 662 * target The target of the event. 663 * @param {Object} 664 * options Options for the event. Either keyCode or charCode are 665 * required. 666 * @method fireKeyEvent 667 * @static 668 */ 669 fireKeyEvent:function ( type /* :String */, target /* :HTMLElement */, options /* :Object */ ) /* :Void */ { 670 options = options || {}; 671 this.simulateKeyEvent( target, type, options.bubbles, 672 options.cancelable, options.view, options.ctrlKey, 673 options.altKey, options.shiftKey, options.metaKey, 674 options.keyCode, options.charCode ); 675 }, 676 677 /** 678 * Simulates a keydown event on a particular element. 679 * 680 * @param {HTMLElement} 681 * target The element to act on. 682 * @param {Object} 683 * options Additional event options (use DOM standard names). 684 * @method keydown 685 * @static 686 */ 687 keydown:function ( target /* :HTMLElement */, options /* :Object */ ) /* :Void */ { 688 this.fireKeyEvent( "keydown", target, options ); 689 }, 690 691 /** 692 * Simulates a keypress on a particular element. 693 * 694 * @param {HTMLElement} 695 * target The element to act on. 696 * @param {Object} 697 * options Additional event options (use DOM standard names). 698 * @method keypress 699 * @static 700 */ 701 keypress:function ( target /* :HTMLElement */, options /* :Object */ ) /* :Void */ { 702 this.fireKeyEvent( "keypress", target, options ); 703 }, 704 705 /** 706 * Simulates a keyup event on a particular element. 707 * 708 * @param {HTMLElement} 709 * target The element to act on. 710 * @param {Object} 711 * options Additional event options (use DOM standard names). 712 * @method keyup 713 * @static 714 */ 715 keyup:function ( target /* :HTMLElement */, options /* Object */ ) /* :Void */ { 716 this.fireKeyEvent( "keyup", target, options ); 717 }, 718 719 /** 720 * 提供iframe扩展支持,用例测试需要独立场景的用例,由于异步支持,通过finish方法触发start 721 * <li>事件绑定在frame上,包括afterfinish和jsloaded 722 * 723 * @param op.win 724 * @param op.nojs 725 * 不加载额外js 726 * @param op.ontest 727 * 测试步骤 728 * @param op.onbeforestart 729 * 测试启动前处理步骤,默认为QUnit.stop(); 730 * @param op.onafterfinish 731 * 测试完毕执行步骤,默认为QUnit.start() 732 * 733 */ 734 frameExt:function ( op ) { 735 stop(); 736 op = typeof op == 'function' ? { 737 ontest:op 738 } : op; 739 var pw = op.win || window, w, f, url = '', id = typeof op.id == 'undefined' ? 'f' 740 : op.id, fid = 'iframe#' + id; 741 742 op.finish = function () { 743 pw.$( fid ).unbind(); 744 setTimeout( function () { 745 pw.$( 'div#div' + id ).remove(); 746 start(); 747 }, 20 ); 748 }; 749 750 if ( pw.$( fid ).length == 0 ) { 751 /* 添加frame,部分情况下,iframe没有边框,为了可以看到效果,添加一个带边框的div */ 752 pw.$( pw.document.body ).append( '<div id="div' + id + '"></div>' ); 753 pw.$( 'div#div' + id ).append( '<iframe id="' + id + '"></iframe>' ); 754 } 755 op.onafterstart && op.onafterstart( $( 'iframe#f' )[0] ); 756 pw.$( 'script' ).each( function () { 757 if ( this.src && this.src.indexOf( 'import.php' ) >= 0 ) { 758 url = this.src.split( 'import.php' )[1]; 759 } 760 } ); 761 pw.$( fid ).one( 'load', 762 function ( e ) { 763 var w = e.target.contentWindow; 764 var h = setInterval( function () { 765 if ( w.baidu ) {// 等待加载完成,IE6下这地方总出问题 766 clearInterval( h ); 767 op.ontest( w, w.frameElement ); 768 } 769 }, 20 ); 770 // 找到当前操作的iframe,然后call ontest 771 } ).attr( 'src', cpath + 'frame.php' + url ); 772 }, 773 774 /** 775 * 776 * 判断2个数组是否相等 777 * 778 * @static 779 */ 780 isEqualArray:function ( array1, array2 ) { 781 if ( '[object Array]' != Object.prototype.toString.call( array1 ) 782 || '[object Array]' != Object.prototype.toString.call( array2 ) ) 783 return (array1 === array2); 784 else if ( array1.length != array2.length ) 785 return false; 786 else { 787 for ( var i in array1 ) { 788 if ( array1[i] != array2[i] ) 789 return false; 790 } 791 return true; 792 } 793 }, 794 795 /*************************************************************************** 796 * 797 * 通用数据模块 798 * 799 * @static 800 * 801 **************************************************************************/ 802 commonData:{// 针对测试文件的路径而不是UserAction的路径 803 "testdir":'../../', 804 datadir:(function () { 805 return location.href.split( "/_test/" )[0] + "/_test/tools/data/"; 806 })(), 807 currentPath:function () { 808 var params = location.search.substring( 1 ).split( '&' ); 809 for ( var i = 0; i < params.length; i++ ) { 810 var p = params[i]; 811 if ( p.split( '=' )[0] == 'case' ) { 812 var casepath = p.split( '=' )[1].split( '.' ).join( '/' ); 813 return location.href.split( '/_test/' )[0] + '/_test/' 814 + casepath.substring( 0, casepath.lastIndexOf( '/' ) ) 815 + '/'; 816 } 817 } 818 return ""; 819 } 820 }, 821 822 importsrc:function ( src, callback, matcher, exclude, win ) { 823 win = win || window; 824 var doc = win.document; 825 826 var srcpath = location.href.split( "/_test/" )[0] 827 + "/_test/tools/br/import.php"; 828 var param0 = src; 829 var ps = { 830 f:src 831 }; 832 if ( exclude ) 833 ps.e = exclude; 834 var param1 = exclude || ""; 835 /** 836 * IE下重复载入会出现无法执行情况 837 */ 838 if ( win.execScript ) { 839 $.get( srcpath, ps, function ( data ) { 840 win.execScript( data ); 841 } ); 842 } else { 843 var head = doc.getElementsByTagName( 'head' )[0]; 844 var sc = doc.createElement( 'script' ); 845 sc.type = 'text/javascript'; 846 sc.src = srcpath + "?f=" + param0 + "&e=" + param1; 847 head.appendChild( sc ); 848 } 849 850 matcher = matcher || src; 851 var mm = matcher.split( "," )[0].split( "." ); 852 var h = setInterval( function () { 853 var p = win; 854 for ( var i = 0; i < mm.length; i++ ) { 855 if ( typeof (p[mm[i]]) == 'undefined' ) { 856 // console.log(mm[i]); 857 return; 858 } 859 p = p[mm[i]]; 860 } 861 clearInterval( h ); 862 if ( callback && 'function' == typeof callback ) 863 callback(); 864 }, 20 ); 865 }, 866 867 /* 用于加载css文件,如果没有加载完毕则不执行回调函数 */ 868 loadcss:function ( url, callback, classname, style, value ) { 869 var links = document.getElementsByTagName( 'link' ); 870 for ( var link in links ) { 871 if ( link.href == url ) { 872 callback(); 873 return; 874 } 875 } 876 var head = document.getElementsByTagName( 'head' )[0]; 877 var link = head.appendChild( document.createElement( 'link' ) ); 878 link.setAttribute( "rel", "stylesheet" ); 879 link.setAttribute( "type", "text/css" ); 880 link.setAttribute( "href", url ); 881 var div = document.body.appendChild( document.createElement( "div" ) ); 882 $( document ).ready( 883 function () { 884 div.className = classname || 'cssloaded'; 885 var h = setInterval( function () { 886 if ( $( div ).css( style || 'width' ) == value 887 || $( div ).css( style || 'width' ) == '20px' ) { 888 clearInterval( h ); 889 document.body.removeChild( div ); 890 setTimeout( callback, 20 ); 891 } 892 }, 20 ); 893 } ); 894 }, 895 896 /** 897 * options supported 898 */ 899 delayhelper:function ( oncheck, onsuccess, onfail, timeout ) { 900 onsuccess = onsuccess || oncheck.onsuccess; 901 onfail = onfail || oncheck.onfail || function () { 902 window.QUnit.fail( 'timeout wait for timeout : ' + timeout + 'ms' ); 903 start(); 904 }; 905 timeout = timeout || oncheck.timeout || 10000; 906 907 oncheck = (typeof oncheck == 'function') ? oncheck : oncheck.oncheck; 908 var h1 = setInterval( function () { 909 if ( !oncheck() ) 910 return; 911 else { 912 clearInterval( h1 ); 913 clearTimeout( h2 ); 914 typeof onsuccess == "function" && onsuccess(); 915 } 916 }, 20 ); 917 var h2 = setTimeout( function () { 918 clearInterval( h1 ); 919 clearTimeout( h2 ); 920 onfail(); 921 }, timeout ); 922 }, 923 924 browser:(function () { 925 var win = window; 926 927 var numberify = function ( s ) { 928 var c = 0; 929 return parseFloat( s.replace( /\./g, function () { 930 return (c++ == 1) ? '' : '.'; 931 } ) ); 932 }, 933 934 nav = win && win.navigator, 935 936 o = { 937 938 /** 939 * Internet Explorer version number or 0. Example: 6 940 * 941 * @property ie 942 * @type float 943 * @static 944 */ 945 ie:0, 946 947 /** 948 * Opera version number or 0. Example: 9.2 949 * 950 * @property opera 951 * @type float 952 * @static 953 */ 954 opera:0, 955 956 /** 957 * Gecko engine revision number. Will evaluate to 1 if Gecko is 958 * detected but the revision could not be found. Other browsers will 959 * be 0. Example: 1.8 960 * 961 * <pre> 962 * Firefox 1.0.0.4: 1.7.8 <-- Reports 1.7 963 * Firefox 1.5.0.9: 1.8.0.9 <-- 1.8 964 * Firefox 2.0.0.3: 1.8.1.3 <-- 1.81 965 * Firefox 3.0 <-- 1.9 966 * Firefox 3.5 <-- 1.91 967 * </pre> 968 * 969 * @property gecko 970 * @type float 971 * @static 972 */ 973 gecko:0, 974 975 /** 976 * AppleWebKit version. KHTML browsers that are not WebKit browsers 977 * will evaluate to 1, other browsers 0. Example: 418.9 978 * 979 * <pre> 980 * Safari 1.3.2 (312.6): 312.8.1 <-- Reports 312.8 -- currently the 981 * latest available for Mac OSX 10.3. 982 * Safari 2.0.2: 416 <-- hasOwnProperty introduced 983 * Safari 2.0.4: 418 <-- preventDefault fixed 984 * Safari 2.0.4 (419.3): 418.9.1 <-- One version of Safari may run 985 * different versions of webkit 986 * Safari 2.0.4 (419.3): 419 <-- Tiger installations that have been 987 * updated, but not updated 988 * to the latest patch. 989 * Webkit 212 nightly: 522+ <-- Safari 3.0 precursor (with native SVG 990 * and many major issues fixed). 991 * Safari 3.0.4 (523.12) 523.12 <-- First Tiger release - automatic update 992 * from 2.x via the 10.4.11 OS patch 993 * Webkit nightly 1/2008:525+ <-- Supports DOMContentLoaded event. 994 * yahoo.com user agent hack removed. 995 * </pre> 996 * 997 * http://en.wikipedia.org/wiki/Safari_version_history 998 * 999 * @property webkit 1000 * @type float 1001 * @static 1002 */ 1003 webkit:0, 1004 1005 /** 1006 * Chrome will be detected as webkit, but this property will also be 1007 * populated with the Chrome version number 1008 * 1009 * @property chrome 1010 * @type float 1011 * @static 1012 */ 1013 chrome:0, 1014 1015 safari:0, 1016 1017 firefox:0, 1018 1019 maxthon:0, 1020 maxthonIE:0, 1021 1022 /** 1023 * The mobile property will be set to a string containing any 1024 * relevant user agent information when a modern mobile browser is 1025 * detected. Currently limited to Safari on the iPhone/iPod Touch, 1026 * Nokia N-series devices with the WebKit-based browser, and Opera 1027 * Mini. 1028 * 1029 * @property mobile 1030 * @type string 1031 * @static 1032 */ 1033 mobile:null, 1034 1035 /** 1036 * Adobe AIR version number or 0. Only populated if webkit is 1037 * detected. Example: 1.0 1038 * 1039 * @property air 1040 * @type float 1041 */ 1042 air:0, 1043 1044 /** 1045 * Google Caja version number or 0. 1046 * 1047 * @property caja 1048 * @type float 1049 */ 1050 caja:nav && nav.cajaVersion, 1051 1052 /** 1053 * Set to true if the pagebreak appears to be in SSL 1054 * 1055 * @property secure 1056 * @type boolean 1057 * @static 1058 */ 1059 secure:false, 1060 1061 /** 1062 * The operating system. Currently only detecting windows or 1063 * macintosh 1064 * 1065 * @property os 1066 * @type string 1067 * @static 1068 */ 1069 os:null 1070 1071 }, 1072 1073 ua = nav && nav.userAgent, 1074 1075 loc = win && win.location, 1076 1077 href = loc && loc.href, 1078 1079 m; 1080 1081 o.secure = href && (href.toLowerCase().indexOf( "https" ) === 0); 1082 1083 if ( ua ) { 1084 1085 if ( (/windows|win32/i).test( ua ) ) { 1086 o.os = 'windows'; 1087 } else if ( (/macintosh/i).test( ua ) ) { 1088 o.os = 'macintosh'; 1089 } else if ( (/rhino/i).test( ua ) ) { 1090 o.os = 'rhino'; 1091 } 1092 1093 // Modern KHTML browsers should qualify as Safari X-Grade 1094 if ( (/KHTML/).test( ua ) ) { 1095 o.webkit = 1; 1096 } 1097 if ( window.external && /(\d+\.\d)/.test( external.max_version ) ) { 1098 1099 o.maxthon = parseFloat( RegExp['\x241'] ); 1100 if ( /MSIE/.test( ua ) ) { 1101 o.maxthonIE = 1; 1102 o.maxthon = 0; 1103 } 1104 1105 } 1106 // Modern WebKit browsers are at least X-Grade 1107 m = ua.match( /AppleWebKit\/([^\s]*)/ ); 1108 if ( m && m[1] ) { 1109 o.webkit = numberify( m[1] ); 1110 1111 // Mobile browser check 1112 if ( / Mobile\//.test( ua ) ) { 1113 o.mobile = "Apple"; // iPhone or iPod Touch 1114 } else { 1115 m = ua.match( /NokiaN[^\/]*|Android \d\.\d|webOS\/\d\.\d/ ); 1116 if ( m ) { 1117 o.mobile = m[0]; // Nokia N-series, Android, webOS, 1118 // ex: 1119 // NokiaN95 1120 } 1121 } 1122 1123 var m1 = ua.match( /Safari\/([^\s]*)/ ); 1124 if ( m1 && m1[1] ) // Safari 1125 o.safari = numberify( m1[1] ); 1126 m = ua.match( /Chrome\/([^\s]*)/ ); 1127 if ( o.safari && m && m[1] ) { 1128 o.chrome = numberify( m[1] ); // Chrome 1129 } else { 1130 m = ua.match( /AdobeAIR\/([^\s]*)/ ); 1131 if ( m ) { 1132 o.air = m[0]; // Adobe AIR 1.0 or better 1133 } 1134 } 1135 } 1136 1137 if ( !o.webkit ) { // not webkit 1138 // @todo check Opera/8.01 (J2ME/MIDP; Opera Mini/2.0.4509/1316; 1139 // fi; U; 1140 // try get firefox and it's ver 1141 // ssr) 1142 m = ua.match( /Opera[\s\/]([^\s]*)/ ); 1143 if ( m && m[1] ) { 1144 o.opera = numberify( m[1] ); 1145 m = ua.match( /Opera Mini[^;]*/ ); 1146 if ( m ) { 1147 o.mobile = m[0]; // ex: Opera Mini/2.0.4509/1316 1148 } 1149 } else { // not opera or webkit 1150 m = ua.match( /MSIE\s([^;]*)/ ); 1151 if ( m && m[1] ) { 1152 o.ie = numberify( m[1] ); 1153 } else { // not opera, webkit, or ie 1154 m = ua.match( /Gecko\/([^\s]*)/ ); 1155 if ( m ) { 1156 o.gecko = 1; // Gecko detected, look for revision 1157 m = ua.match( /rv:([^\s\)]*)/ ); 1158 if ( m && m[1] ) { 1159 o.gecko = numberify( m[1] ); 1160 } 1161 } 1162 } 1163 } 1164 } 1165 } 1166 1167 return o; 1168 } 1169 ) 1170 (), 1171 1172 /** 1173 * 提供队列方式执行用例的方案,接口包括start、add、next,方法全部执行完毕时会启动用例继续执行 1174 */ 1175 functionListHelper:function () { 1176 var check = { 1177 list:[], 1178 start:function () { 1179 var self = this; 1180 $( this ).bind( 'next', function () { 1181 setTimeout( function () {// 避免太深的堆栈 1182 if ( self.list.length == 0 ) 1183 start(); 1184 else 1185 self.list.shift()(); 1186 }, 0 ); 1187 } ); 1188 self.next(); 1189 }, 1190 add:function ( func ) { 1191 this.list.push( func ); 1192 }, 1193 next:function ( delay ) { 1194 var self = this; 1195 if ( delay ) { 1196 setTimeout( function () { 1197 $( self ).trigger( 'next' ); 1198 }, delay ); 1199 } else 1200 $( this ).trigger( 'next' ); 1201 } 1202 }; 1203 return check; 1204 }, 1205 getHTML:function ( co ) { 1206 var div = document.createElement( 'div' ), h; 1207 if ( !co ) 1208 return 'null'; 1209 div.appendChild( co.cloneNode( true ) ); 1210 h = div.innerHTML.toLowerCase(); 1211 1212 h = h.replace( /[\r\n\t\u200b\ufeff]/g, '' ); // Remove line feeds and tabs 1213 h = h.replace( / (\w+)=([^\"][^\s>]*)/gi, ' $1="$2"' ); // Restore 1214 // attribs on IE 1215 return h; 1216 }, 1217 getChildHTML:function ( co ) { 1218 1219 var h = co.innerHTML.toLowerCase(); 1220 1221 h = h.replace( /[\r\n\t\u200b\ufeff]/g, '' ); // Remove line feeds and tabs 1222 h = h.replace( / (\w+)=([^\"][^\s>]*)/gi, ' $1="$2"' ); // Restore attribs on IE 1223 1224 return h.replace( /\u200B/g, '' ); 1225 }, 1226 getIndex:function ( node ) { 1227 var childNodes = node.parentNode.childNodes, i = 0; 1228 while ( childNodes[i] !== node ) 1229 i++; 1230 return i; 1231 }, 1232 checkResult:function ( range, sc, ec, so, eo, collapsed, descript ) { 1233 descript = descript ? descript : ''; 1234 equal( range.collapsed, collapsed, "check collapsed --" + descript ); 1235 ok( range.startContainer === sc, "check startContainer--" + descript ); 1236 ok( range.endContainer === ec, "check endContainer--" + descript ); 1237 equal( range.startOffset, so, "check startOffset--" + descript ); 1238 equal( range.endOffset, eo, "check endOffset--" + descript ); 1239 }, 1240 manualDeleteFillData:function ( node ) { 1241 var childs = node.childNodes; 1242 for ( var i = 0; i < childs.length; i++ ) { 1243 var fillData = childs[i]; 1244 if ( (fillData.nodeType == 3) && ( fillData.data == domUtils.fillChar ) ) { 1245 domUtils.remove( fillData ); 1246 fillData = null; 1247 1248 } 1249 else 1250 this.manualDeleteFillData( fillData ); 1251 } 1252 1253 1254 }, 1255 cssStyleToDomStyle:function ( cssName ) { 1256 var test = document.createElement( 'div' ).style, 1257 cssFloat = test.cssFloat != undefined ? 'cssFloat' 1258 : test.styleFloat != undefined ? 'styleFloat' 1259 : 'float', 1260 cache = { 'float':cssFloat }; 1261 1262 function replacer( match ) { 1263 return match.charAt( 1 ).toUpperCase(); 1264 } 1265 1266 // return function( cssName ) { 1267 return cache[cssName] || (cache[cssName] = cssName.replace( /-./g, replacer ) ); 1268 // }; 1269 }, 1270 isSameStyle:function ( elementA, elementB ) { 1271 var styleA = elementA.style.cssText, 1272 styleB = elementB.style.cssText; 1273 if ( this.browser.ie && this.browser.version == 6 ) { 1274 styleA = styleA.toLowerCase(); 1275 styleB = styleB.toLowerCase(); 1276 } 1277 if ( !styleA && !styleB ) { 1278 return true; 1279 } else if ( !styleA || !styleB ) { 1280 return false; 1281 } 1282 var styleNameMap = {}, 1283 record = [], 1284 exit = {}; 1285 styleA.replace( /[\w-]+\s*(?=:)/g, function ( name ) { 1286 styleNameMap[name] = record.push( name ); 1287 } ); 1288 try { 1289 styleB.replace( /[\w-]+\s*(?=:)/g, function ( name ) { 1290 var index = styleNameMap[name]; 1291 if ( index ) { 1292 // name = this.cssStyleToDomStyle( name ); 1293 if ( elementA.style[name] !== elementB.style[name] ) { 1294 throw exit; 1295 } 1296 record[index - 1] = ''; 1297 } else { 1298 throw exit; 1299 } 1300 } ); 1301 } catch ( ex ) { 1302 if ( ex === exit ) { 1303 return false; 1304 } 1305 } 1306 return !record.join( '' ); 1307 }, 1308 hasSameAttrs:function ( nodeA, nodeB ) { 1309 if ( nodeA.tagName != nodeB.tagName ) 1310 return 0; 1311 var thisAttribs = nodeA.attributes, 1312 otherAttribs = nodeB.attributes; 1313 if ( thisAttribs.length != otherAttribs.length ) 1314 return 0; 1315 if ( thisAttribs.length == 0 ) 1316 return 1; 1317 var attrA, attrB; 1318 for ( var i = 0; attrA = thisAttribs[i++]; ) { 1319 if ( attrA.nodeName == 'style' ) { 1320 if ( this.isSameStyle( nodeA, nodeB ) ) { 1321 continue 1322 } else { 1323 return 0; 1324 } 1325 } 1326 if ( !ua.browser.ie || attrA.specified ) { 1327 attrB = nodeB.attributes[attrA.nodeName]; 1328 if ( !attrB ) { 1329 return 0; 1330 } 1331 } 1332 return 1; 1333 } 1334 return 1; 1335 }, 1336 /** 1337 *检查两个节点(包含所有子节点)是否具有相同的属性 1338 */ 1339 flag:true, 1340 checkAllChildAttribs:function ( nodeA, nodeB ) { 1341 var k = nodeA.childNodes.length; 1342 if ( k != nodeB.childNodes.length ) 1343 this.flag = false; 1344 if ( !this.flag ) 1345 return this.flag; 1346 while ( k ) { 1347 var tmpNodeA = nodeA.childNodes[k - 1]; 1348 var tmpNodeB = nodeB.childNodes[k - 1]; 1349 k--; 1350 1351 if ( tmpNodeA.nodeType == 3 || tmpNodeB.nodeType == 3 || tmpNodeA.nodeType == 8 || tmpNodeB.nodeType == 8 ) 1352 continue; 1353 if ( !this.hasSameAttrs( tmpNodeA, tmpNodeB ) ) { 1354 this.flag = false; 1355 break; 1356 1357 } 1358 1359 this.checkAllChildAttribs( tmpNodeA, tmpNodeB ); 1360 } 1361 return this.flag; 1362 }, 1363 haveSameAllChildAttribs:function ( nodeA, nodeB ) { 1364 this.flag = true; 1365 return this.checkAllChildAttribs( nodeA, nodeB ); 1366 }, 1367 /*查看传入的html是否与传入的元素ele具有相同的style*/ 1368 checkHTMLSameStyle:function ( html, doc, ele, descript ) { 1369 var tagEle = doc.createElement( ele.tagName ); 1370 tagEle.innerHTML = html; 1371 /*会有一些不可见字符,在比较前提前删掉*/ 1372 this.manualDeleteFillData( ele ); 1373 ok( this.haveSameAllChildAttribs( ele, tagEle ), descript ); 1374 // ok(this.equalsNode(ele.innerHMTL,html),descript); 1375 }, 1376 1377 1378 equalsNode:function ( na, nb ) { 1379 function compare( nodeA, nodeB ) { 1380 if ( nodeA.nodeType != nodeB.nodeType ) { 1381 return 0; 1382 } 1383 if ( nodeA.nodeType == 3 ) { 1384 return nodeA.nodeValue == nodeB.nodeValue 1385 } 1386 if ( domUtils.isSameElement( nodeA, nodeB ) ) { 1387 if ( !nodeA.firstChild && !nodeB.firstChild ) { 1388 return 1; 1389 } 1390 if ( nodeA.firstChild && !nodeB.firstChild || !nodeA.firstChild && nodeB.firstChild ) { 1391 return 0 1392 } 1393 for ( var i = 0, ai, bi; ai = nodeA.childNodes[i], bi = nodeB.childNodes[i++]; ) { 1394 1395 if ( !compare( ai, bi ) ) { 1396 return 0 1397 } 1398 } 1399 return 1; 1400 } else { 1401 return 0; 1402 } 1403 } 1404 1405 return compare( domUtils.creElm( document, 'div', { 1406 'innerHTML':na 1407 } ), domUtils.creElm( document, 'div', { 1408 'innerHTML':nb 1409 } ) ); 1410 }, 1411 1412 1413 getSelectedText:function () { 1414 if ( window.getSelection ) { 1415 // This technique is the most likely to be standardized. 1416 // getSelection() returns a Selection object, which we do not document. 1417 return window.getSelection().toString(); 1418 } 1419 else if ( document.getSelection ) { 1420 // This is an older, simpler technique that returns a string 1421 return document.getSelection(); 1422 } 1423 else if ( document.selection ) { 1424 // This is the IE-specific technique. 1425 // We do not document the IE selection property or TextRange objects. 1426 return document.selection.createRange().text; 1427 } 1428 }, 1429 findPosition:function ( oElement ) { 1430 var x2 = 0; 1431 var y2 = 0; 1432 var width = oElement.offsetWidth; 1433 var height = oElement.offsetHeight; 1434 if ( typeof( oElement.offsetParent ) != 'undefined' ) { 1435 for ( var posX = 0, posY = 0; oElement; oElement = oElement.offsetParent ) { 1436 posX += oElement.offsetLeft; 1437 posY += oElement.offsetTop; 1438 } 1439 x2 = posX + width; 1440 y2 = posY + height; 1441 return [ posX, posY , x2, y2]; 1442 1443 } else { 1444 x2 = oElement.x + width; 1445 y2 = oElement.y + height; 1446 return [ oElement.x, oElement.y, x2, y2]; 1447 } 1448 }, 1449 1450 checkElementPath:function ( arr1, arr2, descript ) { 1451 if ( !descript ) 1452 descript = ''; 1453 var index = arr1.length; 1454 if ( index != arr2.length ) 1455 ok( false, '路径深度不相同' ); 1456 else { 1457 1458 while ( index > 0 ) 1459 equal( arr1[--index ], arr2[index ], descript + '---第' + index + '个元素' + arr1[index] ); 1460 } 1461 }, 1462 getBrowser:function () { 1463 var browser = ""; 1464 if ( this.browser.ie == 6 ) 1465 browser = 'ie6'; 1466 if ( this.browser.ie == 7 ) 1467 browser = 'ie7'; 1468 if ( this.browser.ie == 8 ) 1469 browser = 'ie8'; 1470 if ( this.browser.ie == 9 ) 1471 browser = 'ie9'; 1472 if ( this.browser.safari ) 1473 browser = 'safari'; 1474 if ( this.browser.firefox ) 1475 browser = 'firefox'; 1476 if ( this.browser.chrome ) 1477 browser = 'chrome'; 1478 if ( this.browser.maxthon ) { 1479 browser = 'maxthon'; 1480 } 1481 if ( this.browser.maxthonIE ) 1482 browser = 'maxIE'; 1483 if ( this.browser.opera ) 1484 browser = 'opera'; 1485 return browser; 1486 }, 1487 getFloatStyle:function ( ele ) { 1488 if ( this.browser.ie ) 1489 return ele.style['styleFloat']; 1490 else 1491 return ele.style['cssFloat']; 1492 } 1493 1494 1495 }; 1496 var ua = UserAction; 1497 var upath = ua.commonData.currentPath(); 1498 var cpath = ua.commonData.datadir;